Changeset cd4e48 in indico
- Timestamp:
- 08/23/11 15:42:07 (22 months ago)
- Branches:
- master, hello-world-walkthrough, ipv6, v0.98-series, v0.98.2, v0.98.3, v0.98b2, v0.99, b8c30da8ebdbdcbd675a873997cc3e95f567de49, 4287315ec967a3da168d83963c14001db8487d53
- Children:
- 05e29d
- Parents:
- 64b4b4
- git-author:
- Jose Benito <jose.benito.gonzalez@…> (08/23/11 10:27:33)
- git-committer:
- Jose Benito <jose.benito.gonzalez@…> (08/23/11 15:42:07)
- Files:
-
- 2 deleted
- 20 edited
-
contrib/http_api/buildurl.php (deleted)
-
contrib/http_api/buildurl.py (deleted)
-
doc/guides/ExportAPI/access.rst (modified) (3 diffs)
-
doc/guides/ExportAPI/common.rst (modified) (1 diff)
-
doc/guides/ExportAPI/exporters/categ.rst (modified) (1 diff)
-
doc/guides/ExportAPI/exporters/event.rst (modified) (3 diffs)
-
doc/guides/ExportAPI/exporters/reservation.rst (modified) (1 diff)
-
doc/guides/ExportAPI/exporters/room.rst (modified) (2 diffs)
-
doc/guides/UserGuide/Export.rst (modified) (1 diff)
-
indico/MaKaC/common/indexes.py (modified) (3 diffs)
-
indico/MaKaC/common/mail.py (modified) (1 diff)
-
indico/MaKaC/plugins/RoomBooking/export.py (modified) (12 diffs)
-
indico/MaKaC/plugins/base.py (modified) (6 diffs)
-
indico/MaKaC/webinterface/rh/conferenceDisplay.py (modified) (1 diff)
-
indico/MaKaC/webinterface/tpls/AdminAPIKeys.tpl (modified) (1 diff)
-
indico/MaKaC/webinterface/tpls/AdminAPIOptions.tpl (modified) (2 diffs)
-
indico/MaKaC/webinterface/tpls/UserAPI.tpl (modified) (3 diffs)
-
indico/web/http_api/__init__.py (modified) (1 diff)
-
indico/web/http_api/export.py (modified) (15 diffs)
-
indico/web/http_api/handlers.py (modified) (5 diffs)
-
indico/web/http_api/util.py (modified) (2 diffs)
-
setup.py (modified) (1 diff)
Legend:
- Unmodified
- Added
- Removed
-
doc/guides/ExportAPI/access.rst
re0a8fc rcd4e48 17 17 * *WHAT* is the element you want to export (one of *categ*, *event*, *room*, *reservation*) 18 18 * *LOC* is the location of the element(s) specified by *ID* and only used 19 for certain elements .20 * *ID* is the ID of the element you want to export (can be a *-* separated list) 19 for certain elements, for example, for the room booking (https://indico.server/export/room/CERN/120.json?ak=0...) 20 * *ID* is the ID of the element you want to export (can be a *-* separated list). As for example, the 120 in the above URL. 21 21 * *TYPE* is the output format (one of *json*, *jsonp*, *xml*, *html*, *ics*, *atom*) 22 22 * *PARAMS* are various parameters affecting (filtering, sorting, ...) the … … 24 24 * *KEY*, *TS*, *SIG* are part of the :ref:`api-authentication`. 25 25 26 27 Some examples could be: 28 29 * Export data about events in a category: https://my.indico/export/categ/2.json?from=today&to=today&pretty=yes 30 * Export data about a event: https://indico.server/export/event/137346.json?occ=yes&pretty=yes 31 * Export data about rooms: https://indico.server/export/room/CERN/120.json?ak=00000000-0000-0000-0000-000000000000&pretty=yes 32 * Export your reservations: https://indico.server/export/reservation/CERN.json?ak=00000000-0000-0000-0000-000000000000&detail=reservations&from=today&to=today&bookedfor=USERNAME&pretty=yes 33 34 35 See more details about querying in `Exporters <exporters/index.html>`_. 26 36 27 37 .. _api-authentication: … … 68 78 the generated URL as it is likely to expire soon. 69 79 70 You can find example code for Python and PHP in the *contrib/http_api* 71 folder. 80 You can find example code for Python and PHP in the following sections. 81 82 Request Signing for Python 83 ^^^^^^^^^^^^^^^^^^^^^^^^^^ 84 85 A simple example in Python:: 86 87 import hashlib 88 import hmac 89 import urllib 90 import time 91 92 93 def build_indico_request(path, params, api_key=None, secret_key=None, only_public=False): 94 items = params.items() if hasattr(params, 'items') else list(params) 95 if api_key: 96 items.append(('apikey', api_key)) 97 if only_public: 98 items.append(('onlypublic', 'yes')) 99 if secret_key: 100 items.append(('timestamp', str(int(time.time())))) 101 items = sorted(items, key=lambda x: x[0].lower()) 102 url = '%s?%s' % (path, urllib.urlencode(items)) 103 signature = hmac.new(secret_key, url, hashlib.sha1).hexdigest() 104 items.append(('signature', signature)) 105 if not items: 106 return path 107 return '%s?%s' % (path, urllib.urlencode(items)) 108 109 110 if __name__ == '__main__': 111 API_KEY = '00000000-0000-0000-0000-000000000000' 112 SECRET_KEY = '00000000-0000-0000-0000-000000000000' 113 PATH = '/export/categ/1337.json' 114 PARAMS = { 115 'nocache': 'yes', 116 'limit': 123 117 } 118 print build_indico_request(PATH, PARAMS, API_KEY, SECRET_KEY) 119 120 Request Signing for PHP 121 ^^^^^^^^^^^^^^^^^^^^^^^ 122 123 A simple example in PHP:: 124 125 <?php 126 127 function build_indico_request($path, $params, $api_key = null, $secret_key = null, $only_public = false) { 128 if($api_key) { 129 $params['apikey'] = $api_key; 130 } 131 132 if($only_public) { 133 $params['onlypublic'] = 'yes'; 134 } 135 136 if($secret_key) { 137 $params['timestamp'] = time(); 138 uksort($params, 'strcasecmp'); 139 $url = $path . '?' . http_build_query($params); 140 $params['signature'] = hash_hmac('sha1', $url, $secret_key); 141 } 142 143 if(!$params) { 144 return $path; 145 } 146 147 return $path . '?' . http_build_query($params); 148 } 149 150 if(true) { // change to false if you want to include this file 151 $API_KEY = '00000000-0000-0000-0000-000000000000'; 152 $SECRET_KEY = '00000000-0000-0000-0000-000000000000'; 153 $PATH = '/export/categ/1337.json'; 154 $PARAMS = array( 155 'nocache' => 'yes', 156 'limit' => 123 157 ); 158 echo build_indico_request($PATH, $PARAMS, $API_KEY, $SECRET_KEY) . "\n"; 159 } -
doc/guides/ExportAPI/common.rst
re0a8fc rcd4e48 8 8 Param Short Description 9 9 ========== ===== ======================================================= 10 from/to f/t Accepted formats: 11 * ISO 8601 subset - YYYY-MM-DD[THH:MM] 12 * 'today', 'yesterday', 'tomorrow' and 'now' 13 * days in the future/past: '[+/-]DdHHhMMm' 10 14 nocache nc Do not return cached results (the results are written 11 15 to the cache though). -
doc/guides/ExportAPI/exporters/categ.rst
rb69320d rcd4e48 28 28 ~~~~~~ 29 29 30 Returns basic data about the events in the category:: 30 Returns basic data about the events in the category. 31 32 This is the result of the following the query https://my.indico/export/categ/2.json?from=today&to=today&pretty=yes:: 31 33 32 34 { -
doc/guides/ExportAPI/exporters/event.rst
r816a09 rcd4e48 25 25 ~~~~~~ 26 26 Returns basic data about the event. In this example occurrences are 27 included, too. :: 27 included, too. 28 29 Result for https://indico.server/export/event/137346.json?occ=yes&pretty=yes:: 28 30 29 31 { … … 93 95 contributions 94 96 ~~~~~~~~~~~~~ 95 Includes the contributions of the event. :: 97 Includes the contributions of the event. 98 99 Output for https://indico.server/export/event/137346.json?detail=contributions&pretty=yes:: 96 100 97 101 { … … 234 238 sessions. The top-level *contributions* list only contains contributions 235 239 which are not assigned to any session. Subcontributions are included in 236 this details level, too. :: 240 this details level, too. 241 242 For example, https://indico.server/export/event/137346.json?detail=sessions&pretty=yes:: 237 243 238 244 { -
doc/guides/ExportAPI/exporters/reservation.rst
rf2dba1 rcd4e48 35 35 36 36 Returns detailed data about the reservations and the most important 37 information about the booked room:: 37 information about the booked room. 38 39 For example, https://indico.server/export/reservation/CERN.json?ak=00000000-0000-0000-0000-000000000000&detail=reservations&from=today&to=today&bookedfor=*MONNICH*&pretty=yes:: 38 40 39 41 { -
doc/guides/ExportAPI/exporters/room.rst
rf2dba1 rcd4e48 31 31 ~~~~~ 32 32 33 Returns basic data about the rooms:: 33 Returns basic data about the rooms. 34 35 For example, https://indico.server/export/room/CERN/120.json?ak=00000000-0000-0000-0000-000000000000&pretty=yes:: 34 36 35 37 { … … 62 64 ~~~~~~~~~~~~ 63 65 64 Returns basic data about the rooms and their reservations in the given timeframe:: 66 Returns basic data about the rooms and their reservations in the given timeframe. 67 68 Output for https://indico.server/export/room/CERN/120.json?ak=00000000-0000-0000-0000-000000000000&detail=reservations&from=today&to=today&pretty=yes:: 65 69 66 70 { -
doc/guides/UserGuide/Export.rst
re0a8fc rcd4e48 63 63 |image164| 64 64 65 --------------66 67 Using the export.py script68 --------------------------69 70 Indico allows you to programmatically access the content of its71 database by exposing its events through a web service, the72 export.py script.73 74 A typical example of how to use this script is:75 76 http://my.indico.server/tools/export.py?fid=2l12&date=today&days=1000&of=xml77 78 where:79 80 * *fid* is the category from which you want to extract the events (can be a *+* separated list)81 * *date* is the starting date of the event (format: *yyyy-mm-dd* or *today*)82 * *days* is the number of days to export the events (starting on *date*)83 * *of* is the output format (one of *xml*, *html*, *ical*, *rss*)84 85 86 65 Using the HTTP Export API 87 66 ------------------------- 88 67 89 Basides the export.py scriptIndico has an export API which allows you to68 Indico has an export API which allows you to 90 69 export categories, events, rooms and room bookings in various formats such 91 70 as JSON, XML, iCal and Atom. For details no how to use this API, see -
indico/MaKaC/common/indexes.py
rf3a845 rcd4e48 740 740 741 741 def iterateObjectsIn(self, sDate, eDate): 742 """ 743 Returns all the events between two dates taking into account the starting and ending times. 744 """ 742 745 sDay = datetime(sDate.year, sDate.month, sDate.day) if sDate else None 743 746 eDay = datetime(eDate.year, eDate.month, eDate.day) if eDate else None … … 749 752 yield event 750 753 return 751 752 # keep track of the records that have been already sent753 754 754 755 if sDay and int(datetimeToUnixTime(sDay)) in self._idxDay: … … 783 784 784 785 def iterateObjectsInDays(self, sDate=None, eDate=None): 786 """ 787 Returns all the events between two dates WITHOUT taking into account the starting and ending times. 788 """ 785 789 786 790 sDay = int(datetimeToUnixTime(datetime(sDate.year, sDate.month, sDate.day))) if sDate else None -
indico/MaKaC/common/mail.py
refefcc rcd4e48 30 30 @staticmethod 31 31 def send(notification): 32 server=smtplib.SMTP( *Config.getInstance().getSmtpServer())32 server=smtplib.SMTP(Config.getInstance().getSmtpServer()) 33 33 if Config.getInstance().getSmtpUseTLS(): 34 34 server.ehlo() -
indico/MaKaC/plugins/RoomBooking/export.py
r943a54 rcd4e48 24 24 from dateutil import rrule 25 25 from datetime import datetime, timedelta 26 from indico.web.http_api import ExportInterface, Exporter26 from indico.web.http_api import DataFetcher, HTTPAPIHook 27 27 from indico.web.http_api.util import get_query_parameter 28 28 from indico.util.fossilize import fossilize, IFossil … … 38 38 39 39 40 global Exporters = ['RoomExporter', 'ReservationExporter']40 globalHTTPAPIHooks = ['RoomHook', 'ReservationHook'] 41 41 MAX_DATETIME = datetime(2099, 12, 31, 23, 59, 00) 42 42 MIN_DATETIME = datetime(2000, 1, 1, 00, 00, 00) … … 47 47 return utc2server(d) 48 48 49 class RoomBooking Exporter(Exporter):49 class RoomBookingHook(HTTPAPIHook): 50 50 GUEST_ALLOWED = False 51 52 def _getParams(self): 53 super(RoomBookingHook, self)._getParams() 54 55 self._fromDT = utcdate(self._fromDT) if self._fromDT else None 56 self._toDT = utcdate(self._toDT) if self._toDT else None 57 self._occurrences = get_query_parameter(self._queryParams, ['occ', 'occurrences'], 'no') == 'yes' 58 self._resvFilter = getResvStateFilter(self._queryParams) 51 59 52 60 def _hasAccess(self, aw): … … 68 76 return False 69 77 70 class Room Exporter(RoomBookingExporter):78 class RoomHook(RoomBookingHook): 71 79 """ 72 80 Example: /room/CERN/23.xml 73 81 """ 74 82 TYPES = ('room', ) 75 RE = r'(?P<location> \w+)/(?P<idlist>\w+(?:-\w+)*)'83 RE = r'(?P<location>[\w\s]+)/(?P<idlist>\w+(?:-[\w\s]+)*)' 76 84 DEFAULT_DETAIL = 'rooms' 77 85 MAX_RECORDS = { … … 86 94 87 95 def _getParams(self): 88 super(Room Exporter, self)._getParams()89 self._location = self._ urlParams['location']90 self._idList = self._ urlParams['idlist'].split('-')96 super(RoomHook, self)._getParams() 97 self._location = self._pathParams['location'] 98 self._idList = self._pathParams['idlist'].split('-') 91 99 92 100 def export_room(self, aw): 93 expInt = Room ExportInterface(aw, self)94 return expInt.room(self._location, self._idList , self._qdata)95 96 97 class Reservation Exporter(RoomBookingExporter):101 expInt = RoomFetcher(aw, self) 102 return expInt.room(self._location, self._idList) 103 104 105 class ReservationHook(RoomBookingHook): 98 106 TYPES = ('reservation', ) 99 RE = r'(?P<loclist> \w+(?:-\w+)*)'107 RE = r'(?P<loclist>[\w\s]+(?:-[\w\s]+)*)' 100 108 DEFAULT_DETAIL = 'reservations' 101 109 MAX_RECORDS = { … … 109 117 110 118 def _getParams(self): 111 super(Reservation Exporter, self)._getParams()112 self._locList = self._ urlParams['loclist'].split('-')119 super(ReservationHook, self)._getParams() 120 self._locList = self._pathParams['loclist'].split('-') 113 121 114 122 def export_reservation(self, aw): 115 expInt = Reservation ExportInterface(aw, self)116 return expInt.reservation(self._locList , self._qdata)123 expInt = ReservationFetcher(aw, self) 124 return expInt.reservation(self._locList) 117 125 118 126 … … 227 235 228 236 229 class RoomBooking ExportInterface(ExportInterface):237 class RoomBookingFetcher(DataFetcher): 230 238 """ 231 239 Base export interface for RB related stuff 232 240 """ 233 241 234 def _getQueryParams(self, qdata): 235 super(RoomBookingExportInterface, self)._getQueryParams(qdata) 236 237 self._fromDT = utcdate(self._fromDT) if self._fromDT else None 238 self._toDT = utcdate(self._toDT) if self._toDT else None 239 self._occurrences = get_query_parameter(qdata, ['occ', 'occurrences'], 'no') == 'yes' 240 self._resvFilter = getResvStateFilter(qdata) 242 def __init__(self, aw, hook): 243 super(RoomBookingFetcher, self).__init__(aw, hook) 244 self._occurrences = hook._occurrences 245 self._resvFilter = hook._resvFilter 241 246 242 247 @staticmethod … … 266 271 267 272 268 class Room ExportInterface(RoomBookingExportInterface):273 class RoomFetcher(RoomBookingFetcher): 269 274 DETAIL_INTERFACES = { 270 275 'rooms': IRoomMetadataFossil, … … 306 311 return fossil 307 312 308 def room(self, location, idlist, qdata): 309 310 self._getQueryParams(qdata) 313 def room(self, location, idlist): 311 314 312 315 Factory.getDALManager().connect() … … 323 326 324 327 325 class Reservation ExportInterface(RoomBookingExportInterface):328 class ReservationFetcher(RoomBookingFetcher): 326 329 DETAIL_INTERFACES = { 327 330 'reservations': IReservationMetadataFossil … … 332 335 333 336 334 def reservation(self, locList, qdata): 335 336 self._getQueryParams(qdata) 337 def reservation(self, locList): 337 338 338 339 Factory.getDALManager().connect() … … 358 359 359 360 360 def getResvStateFilter(q data):361 cancelled = get_query_parameter(q data, ['cxl', 'cancelled'])362 rejected = get_query_parameter(q data, ['rej', 'rejected'])363 confirmed = get_query_parameter(q data, ['confirmed'], -1)364 archival = get_query_parameter(q data, ['arch', 'archival'])365 repeating = get_query_parameter(q data, ['rec', 'recurring', 'rep', 'repeating'])366 avc = get_query_parameter(q data, ['avc'])367 avcSupport = get_query_parameter(q data, ['avcs', 'avcsupport'])368 bookedFor = get_query_parameter(q data, ['bf', 'bookedfor'])361 def getResvStateFilter(queryParams): 362 cancelled = get_query_parameter(queryParams, ['cxl', 'cancelled']) 363 rejected = get_query_parameter(queryParams, ['rej', 'rejected']) 364 confirmed = get_query_parameter(queryParams, ['confirmed'], -1) 365 archival = get_query_parameter(queryParams, ['arch', 'archival']) 366 repeating = get_query_parameter(queryParams, ['rec', 'recurring', 'rep', 'repeating']) 367 avc = get_query_parameter(queryParams, ['avc']) 368 avcSupport = get_query_parameter(queryParams, ['avcs', 'avcsupport']) 369 bookedFor = get_query_parameter(queryParams, ['bf', 'bookedfor']) 369 370 if not any((cancelled, rejected, confirmed != -1, archival, repeating, avc, avcSupport, bookedFor)): 370 371 return None -
indico/MaKaC/plugins/base.py
rd2f974 rcd4e48 488 488 self.__options = {} 489 489 self.__actions = {} 490 self.__ exporters = []490 self.__HTTPAPIHooks = [] 491 491 492 492 self.__usable = False … … 707 707 ############## end of actions related ############### 708 708 709 ############## exportersrelated ###############710 def updateAll Exporters(self, retrievedPluginExporters):711 self.__ exporters = []712 if retrievedPlugin Exporters is not None:713 self.__ exporters = retrievedPluginExporters709 ############## HTTPAPIHook related ############### 710 def updateAllHTTPAPIHooks(self, retrievedPluginHTTPAPIHooks): 711 self.__HTTPAPIHooks = [] 712 if retrievedPluginHTTPAPIHooks is not None: 713 self.__HTTPAPIHooks = retrievedPluginHTTPAPIHooks 714 714 self._notifyModification() 715 715 716 def get ExporterList(self):716 def getHTTPAPIHookList(self): 717 717 try: 718 return self.__ exporters718 return self.__HTTPAPIHooks 719 719 except: 720 self.__ exporters = []721 return self.__ exporters722 723 ############## end of exporters related ###############720 self.__HTTPAPIHooks = [] 721 return self.__HTTPAPIHooks 722 723 ############## end of HTTPAPIHooks related ############### 724 724 725 725 def _notifyModification(self): … … 804 804 805 805 if hasattr(pluginModule, "export") and \ 806 hasattr(pluginModule.options, "global Exporters"):807 p.updateAll Exporters(pluginModule.export.globalExporters)806 hasattr(pluginModule.options, "globalHTTPAPIHooks"): 807 p.updateAllHTTPAPIHooks(pluginModule.export.globalHTTPAPIHooks) 808 808 809 809 self._updateComponentInfo(p, pluginModule) … … 844 844 self.__visible = ptypeMetadata['visible'] 845 845 846 # components, handlers, options, actions and exporters846 # components, handlers, options, actions and HTTPAPIHooks 847 847 self._updateComponentInfo(self, ptypeModule) 848 848 self._updateRHMapInfo(self, ptypeModule) … … 850 850 self.updateAllOptions(self._retrievePluginTypeOptions()) 851 851 self.updateAllActions(self._retrievePluginTypeActions()) 852 self.updateAll Exporters(self._retrievePluginTypeExporters())852 self.updateAllHTTPAPIHooks(self._retrievePluginTypeHTTPAPIHooks()) 853 853 854 854 … … 918 918 return None 919 919 920 def _retrievePluginType Exporters(self):920 def _retrievePluginTypeHTTPAPIHooks(self): 921 921 922 922 hasExportModule = hasattr(self.getModule(), "export") 923 hasGlobal ExportersVariable = hasExportModule and hasattr(self.getModule().export, "globalExporters")924 if hasExportModule and hasGlobal ExportersVariable:925 return self.getModule().export.global Exporters923 hasGlobalHTTPAPIHooksVariable = hasExportModule and hasattr(self.getModule().export, "globalHTTPAPIHooks") 924 if hasExportModule and hasGlobalHTTPAPIHooksVariable: 925 return self.getModule().export.globalHTTPAPIHooks 926 926 else: 927 927 return None -
indico/MaKaC/webinterface/rh/conferenceDisplay.py
rdfedc8 rcd4e48 1054 1054 pending.setFax(params.get("fax","")) 1055 1055 participation = self._conf.getParticipation() 1056 if participation.alreadyParticipating(pending) != 0 :1056 if participation.alreadyParticipating(pending) != 0 or participation.alreadyPending(pending) != 0: 1057 1057 errorList.append("There is already a participant with the email address '%s' in this meeting." 1058 1058 % pending.getEmail()) -
indico/MaKaC/webinterface/tpls/AdminAPIKeys.tpl
rf9e571 rcd4e48 23 23 <td>${key.getUser().getFullName()}</td> 24 24 <td>${key.getUseCount()}</td> 25 <td>${formatDateTime(key.getLastUsedDT()) if key.getLastUsedDT() else 'Never'}</td>25 <td>${formatDateTime(key.getLastUsedDT()) if key.getLastUsedDT() else _('Never')}</td> 26 26 <td>${_('Yes') if key.isBlocked() else _('No')}</td> 27 27 <td>${ key.getKey() }</td> -
indico/MaKaC/webinterface/tpls/AdminAPIOptions.tpl
rfcdc60 rcd4e48 17 17 <td class="blacktext"> 18 18 <select name="apiMode"> 19 <option value="0"${' selected="selected"' if apiMode == 0 else ''}> Public requests without API key, authenticated requests with API key</option>20 <option value="1"${' selected="selected"' if apiMode == 1 else ''}> All requests require an API key</option>21 <option value="2"${' selected="selected"' if apiMode == 2 else ''}> Public requests without API key, authenticated requests with API key and signature</option>22 <option value="3"${' selected="selected"' if apiMode == 3 else ''}> All requests require an API key, authenticated requests additionally need a signature</option>23 <option value="4"${' selected="selected"' if apiMode == 4 else ''}> All requests require an API key and a signature</option>19 <option value="0"${' selected="selected"' if apiMode == 0 else ''}>${ _("Public requests without API key, authenticated requests with API key") }</option> 20 <option value="1"${' selected="selected"' if apiMode == 1 else ''}>${ _("All requests require an API key") }</option> 21 <option value="2"${' selected="selected"' if apiMode == 2 else ''}>${ _("Public requests without API key, authenticated requests with API key and signature") }</option> 22 <option value="3"${' selected="selected"' if apiMode == 3 else ''}>${ _("All requests require an API key, authenticated requests additionally need a signature") }</option> 23 <option value="4"${' selected="selected"' if apiMode == 4 else ''}>${ _("All requests require an API key and a signature") }</option> 24 24 </select> 25 25 </td> 26 26 </tr> 27 27 <tr> 28 <td class="dataCaptionTD"><span class="dataCaptionFormat"> Cache TTL (seconds)</span></td>28 <td class="dataCaptionTD"><span class="dataCaptionFormat">${ _("Cache TTL (seconds)") }</span></td> 29 29 <td class="blacktext"> 30 30 <input type="text" name="apiCacheTTL" id="apiCacheTTL" value="${apiCacheTTL}" /> … … 32 32 </tr> 33 33 <tr> 34 <td class="dataCaptionTD"><span class="dataCaptionFormat"> Signature TTL (seconds)</span></td>34 <td class="dataCaptionTD"><span class="dataCaptionFormat">${ _("Signature TTL (seconds)") }</span></td> 35 35 <td class="blacktext"> 36 36 <input type="text" name="apiSignatureTTL" id="apiSignatureTTL" value="${apiSignatureTTL}" /> -
indico/MaKaC/webinterface/tpls/UserAPI.tpl
r15afa9 rcd4e48 31 31 <td nowrap class="dataCaptionTD"><span class="dataCaptionFormat">${ _("Last used")}</span></td> 32 32 <td class="blacktext"> 33 ${apiKey.getLastUsedDT() and formatDateTime(apiKey.getLastUsedDT()) or 'Never'if apiKey else _('n/a')}33 ${apiKey.getLastUsedDT() and formatDateTime(apiKey.getLastUsedDT()) or _('Never') if apiKey else _('n/a')} 34 34 </td> 35 35 </tr> … … 37 37 <td nowrap class="dataCaptionTD"><span class="dataCaptionFormat">${ _("Last used by")}</span></td> 38 38 <td class="blacktext"> 39 ${apiKey.getLastUsedIP() or 'n/a'if apiKey else _('n/a')}39 ${apiKey.getLastUsedIP() or _('n/a') if apiKey else _('n/a')} 40 40 </td> 41 41 </tr> … … 93 93 </tr> 94 94 <tr> 95 <td nowrap class="dataCaptionTD"><span class="dataCaptionFormat"> Old keys</span></td>95 <td nowrap class="dataCaptionTD"><span class="dataCaptionFormat">${ _("Old keys") }</span></td> 96 96 <td class="blacktext"> 97 97 % if apiKey.getOldKeys(): -
indico/web/http_api/__init__.py
rd2f974 rcd4e48 23 23 """ 24 24 25 from indico.web.http_api.export import ExportInterface, LimitExceededException, Exporter25 from indico.web.http_api.export import DataFetcher, LimitExceededException, HTTPAPIHook 26 26 27 27 API_MODE_KEY = 0 # public requests without API key, authenticated requests with api key -
indico/web/http_api/export.py
r5dc223 rcd4e48 28 28 import pytz 29 29 import re 30 import urllib 30 31 from zope.interface import Interface, implements 31 32 from datetime import datetime, timedelta, date, time … … 74 75 75 76 76 class Exporter(object): 77 EXPORTER_LIST = [] 77 class HTTPAPIHook(object): 78 """This class is the hook between the query (path+params) and the generator of the results (fossil). 79 It is also in charge of checking the parameters and the access rights. 80 """ 81 82 HOOK_LIST = [] 78 83 TYPES = None # abstract 79 84 RE = None # abstract 80 85 DEFAULT_DETAIL = None # abstract 81 86 MAX_RECORDS = None # abstract 82 SERIALIZER_TYPE_MAP = {} 87 SERIALIZER_TYPE_MAP = {} # maps fossil type names to friendly names (useful for plugins e.g. RoomCERN --> Room) 83 88 VALID_FORMATS = None # None = all formats 84 GUEST_ALLOWED = True 85 86 @classmethod 87 def parseRequest(cls, path, qdata): 88 """Parse a request path and return an exporter and the requested data type.""" 89 exporters = itertools.chain(cls.EXPORTER_LIST, cls._getPluginExporters()) 90 for expCls in exporters: 89 GUEST_ALLOWED = True # When False, it forces authentication 90 91 @classmethod 92 def parseRequest(cls, path, queryParams): 93 """Parse a request path and return a hook and the requested data type.""" 94 path = urllib.unquote(path) 95 hooks = itertools.chain(cls.HOOK_LIST, cls._getPluginHooks()) 96 for expCls in hooks: 91 97 m = expCls._matchPath(path) 92 98 if m: … … 95 101 type = g[0] 96 102 format = g[-1] 97 if format not in ExportInterface.getAllowedFormats():103 if format not in DataFetcher.getAllowedFormats(): 98 104 return None, None 99 105 elif expCls.VALID_FORMATS and format not in expCls.VALID_FORMATS: 100 106 return None, None 101 return expCls(q data, type, gd), format107 return expCls(queryParams, type, gd), format 102 108 return None, None 103 109 104 110 @staticmethod 105 111 def register(cls): 106 """Register a n exporterthat is not part of a plugin.107 108 To use it, simply decorate the exporterclass with this method."""112 """Register a hook that is not part of a plugin. 113 114 To use it, simply decorate the hook class with this method.""" 109 115 assert cls.RE is not None 110 Exporter.EXPORTER_LIST.append(cls)116 HTTPAPIHook.HOOK_LIST.append(cls) 111 117 return cls 112 118 … … 119 125 120 126 @classmethod 121 def _getPlugin Exporters(cls):127 def _getPluginHooks(cls): 122 128 for plugin in PluginsHolder().getPluginTypes(): 123 for expClsName in plugin.get ExporterList():129 for expClsName in plugin.getHTTPAPIHookList(): 124 130 yield getattr(plugin.getModule().export, expClsName) 125 131 126 def __init__(self, q data, type, urlParams):127 self._q data = qdata132 def __init__(self, queryParams, type, pathParams): 133 self._queryParams = queryParams 128 134 self._type = type 129 self._ urlParams = urlParams135 self._pathParams = pathParams 130 136 131 137 def _getParams(self): 132 self._offset = get_query_parameter(self._q data, ['O', 'offset'], 0, integer=True)133 self._orderBy = get_query_parameter(self._q data, ['o', 'order'], 'start')134 self._descending = get_query_parameter(self._q data, ['c', 'descending'], False)135 self._detail = get_query_parameter(self._q data, ['d', 'detail'], self.DEFAULT_DETAIL)136 tzName = get_query_parameter(self._q data, ['tz'], None)138 self._offset = get_query_parameter(self._queryParams, ['O', 'offset'], 0, integer=True) 139 self._orderBy = get_query_parameter(self._queryParams, ['o', 'order'], 'start') 140 self._descending = get_query_parameter(self._queryParams, ['c', 'descending'], False) 141 self._detail = get_query_parameter(self._queryParams, ['d', 'detail'], self.DEFAULT_DETAIL) 142 tzName = get_query_parameter(self._queryParams, ['tz'], None) 137 143 138 144 info = HelperMaKaCInfo.getMaKaCInfoInstance() … … 146 152 raise HTTPAPIError("Bad timezone: '%s'" % e.message, apache.HTTP_BAD_REQUEST) 147 153 max = self.MAX_RECORDS.get(self._detail, 1000) 148 self._userLimit = get_query_parameter(self._q data, ['n', 'limit'], 0, integer=True)154 self._userLimit = get_query_parameter(self._queryParams, ['n', 'limit'], 0, integer=True) 149 155 if self._userLimit > max: 150 156 raise HTTPAPIError("You can only request up to %d records per request with the detail level '%s'" % … … 152 158 self._limit = self._userLimit if self._userLimit > 0 else max 153 159 160 fromDT = get_query_parameter(self._queryParams, ['f', 'from']) 161 toDT = get_query_parameter(self._queryParams, ['t', 'to']) 162 dayDT = get_query_parameter(self._queryParams, ['day']) 163 164 if (fromDT or toDT) and dayDT: 165 raise HTTPAPIError("'day' can only be used without 'from' and 'to'", apache.HTTP_BAD_REQUEST) 166 elif dayDT: 167 fromDT = toDT = dayDT 168 169 self._fromDT = DataFetcher._getDateTime('from', fromDT, self._tz) if fromDT else None 170 self._toDT = DataFetcher._getDateTime('to', toDT, self._tz, aux=self._fromDT) if toDT else None 171 154 172 def _hasAccess(self, aw): 155 173 return True … … 159 177 self._getParams() 160 178 if not self.GUEST_ALLOWED and not aw.getUser(): 161 raise HTTPAPIError('Guest access to this exporteris forbidden.', apache.HTTP_FORBIDDEN)179 raise HTTPAPIError('Guest access to this hook is forbidden.', apache.HTTP_FORBIDDEN) 162 180 if not self._hasAccess(aw): 163 raise HTTPAPIError('Access to this exporteris restricted.', apache.HTTP_FORBIDDEN)181 raise HTTPAPIError('Access to this hook is restricted.', apache.HTTP_FORBIDDEN) 164 182 resultList = [] 165 183 complete = True … … 178 196 179 197 180 class ExportInterface(object):198 class DataFetcher(object): 181 199 DETAIL_INTERFACES = {} 182 200 … … 188 206 'title': lambda x: x.getTitle()} 189 207 190 def __init__(self, aw, exporter):208 def __init__(self, aw, hook): 191 209 self._aw = aw 192 self._tz = exporter._tz 193 self._serverTZ = exporter._serverTZ 194 self._offset = exporter._offset 195 self._limit = exporter._limit 196 self._detail = exporter._detail 197 self._orderBy = exporter._orderBy 198 self._descending = exporter._descending 210 self._tz = hook._tz 211 self._serverTZ = hook._serverTZ 212 self._offset = hook._offset 213 self._limit = hook._limit 214 self._detail = hook._detail 215 self._orderBy = hook._orderBy 216 self._descending = hook._descending 217 self._fromDT = hook._fromDT 218 self._toDT = hook._toDT 199 219 200 220 @classmethod … … 266 286 return tz.localize(value.combine(value.date(), time(23, 59, 59))) 267 287 268 def _getQueryParams(self, qdata):269 fromDT = get_query_parameter(qdata, ['f', 'from'])270 toDT = get_query_parameter(qdata, ['t', 'to'])271 dayDT = get_query_parameter(qdata, ['day'])272 273 if (fromDT or toDT) and dayDT:274 raise HTTPAPIError("'day' can only be used without 'from' and 'to'", apache.HTTP_BAD_REQUEST)275 elif dayDT:276 fromDT = toDT = dayDT277 278 self._fromDT = ExportInterface._getDateTime('from', fromDT, self._tz) if fromDT else None279 self._toDT = ExportInterface._getDateTime('to', toDT, self._tz, aux=self._fromDT) if toDT else None280 281 288 def _limitIterator(self, iterator, limit): 282 289 counter = 0 … … 349 356 350 357 351 @ Exporter.register352 class CategoryEvent Exporter(Exporter):358 @HTTPAPIHook.register 359 class CategoryEventHook(HTTPAPIHook): 353 360 TYPES = ('event', 'categ') 354 361 RE = r'(?P<idlist>\w+(?:-\w+)*)' … … 362 369 363 370 def _getParams(self): 364 super(CategoryEventExporter, self)._getParams() 365 self._idList = self._urlParams['idlist'].split('-') 371 super(CategoryEventHook, self)._getParams() 372 self._idList = self._pathParams['idlist'].split('-') 373 self._occurrences = get_query_parameter(self._queryParams, ['occ', 'occurrences'], 'no') == 'yes' 374 self._location = get_query_parameter(self._queryParams, ['l', 'location']) 375 self._room = get_query_parameter(self._queryParams, ['r', 'room']) 366 376 367 377 def export_categ(self, aw): 368 expInt = CategoryEvent ExportInterface(aw, self)369 return expInt.category(self._idList , self._qdata)378 expInt = CategoryEventFetcher(aw, self) 379 return expInt.category(self._idList) 370 380 371 381 def export_event(self, aw): 372 expInt = CategoryEvent ExportInterface(aw, self)373 return expInt.event(self._idList , self._qdata)374 375 376 class CategoryEvent ExportInterface(ExportInterface):382 expInt = CategoryEventFetcher(aw, self) 383 return expInt.event(self._idList) 384 385 386 class CategoryEventFetcher(DataFetcher): 377 387 DETAIL_INTERFACES = { 378 388 'events': IConferenceMetadataFossil, … … 382 392 } 383 393 384 def _getQueryParams(self, qdata): 385 super(CategoryEventExportInterface, self)._getQueryParams(qdata) 386 self._occurrences = get_query_parameter(qdata, ['occ', 'occurrences'], 'no') == 'yes' 394 def __init__(self, aw, hook): 395 super(CategoryEventFetcher, self).__init__(aw, hook) 396 self._occurrences = hook._occurrences 397 self._location = hook._location 398 self._room = hook._room 387 399 388 400 def _postprocess(self, obj, fossil, iface): … … 412 424 return fossil 413 425 414 def category(self, idlist, qdata): 415 self._getQueryParams(qdata) 416 location = get_query_parameter(qdata, ['l', 'location']) 417 room = get_query_parameter(qdata, ['r', 'room']) 418 426 def category(self, idlist): 419 427 idx = IndexesHolder().getById('categoryDate') 420 428 421 429 filter = None 422 if room orlocation:430 if self._room or self._location: 423 431 def filter(obj): 424 if location:432 if self._location: 425 433 name = obj.getLocation() and obj.getLocation().getName() 426 if not name or not fnmatch.fnmatch(name.lower(), location.lower()):434 if not name or not fnmatch.fnmatch(name.lower(), self._location.lower()): 427 435 return False 428 if room:436 if self._room: 429 437 name = obj.getRoom() and obj.getRoom().getName() 430 if not name or not fnmatch.fnmatch(name.lower(), room.lower()):438 if not name or not fnmatch.fnmatch(name.lower(), self._room.lower()): 431 439 return False 432 440 return True … … 436 444 yield obj 437 445 438 def event(self, idlist, qdata): 439 self._getQueryParams(qdata) 446 def event(self, idlist): 440 447 ch = ConferenceHolder() 441 448 -
indico/web/http_api/handlers.py
r1e4496 rcd4e48 33 33 34 34 # indico imports 35 from indico.web.http_api import Exporter35 from indico.web.http_api import HTTPAPIHook 36 36 from indico.web.http_api.auth import APIKeyHolder 37 37 from indico.web.http_api.cache import RequestCache … … 60 60 Dynamic arguments like signature and timestamp are removed from the query string. 61 61 """ 62 q data= remove_lists(parse_qs(query))62 queryParams = remove_lists(parse_qs(query)) 63 63 if remove: 64 64 for key in remove: 65 q data.pop(key, None)66 sortedQuery = sorted(q data.items(), key=lambda x: x[0].lower())65 queryParams.pop(key, None) 66 sortedQuery = sorted(queryParams.items(), key=lambda x: x[0].lower()) 67 67 if separate: 68 68 return path, sortedQuery and urllib.urlencode(sortedQuery) … … 122 122 path, query = req.URLFields['PATH_INFO'], req.URLFields['QUERY_STRING'] 123 123 # Parse the actual query string 124 q data= parse_qs(query)124 queryParams = parse_qs(query) 125 125 126 126 dbi = DBMgr.getInstance() … … 129 129 cache = RequestCache(HelperMaKaCInfo.getMaKaCInfoInstance().getAPICacheTTL()) 130 130 131 apiKey = get_query_parameter(q data, ['ak', 'apikey'], None)132 signature = get_query_parameter(q data, ['signature'])133 timestamp = get_query_parameter(q data, ['timestamp'], 0, integer=True)134 no_cache = get_query_parameter(q data, ['nc', 'nocache'], 'no') == 'yes'135 pretty = get_query_parameter(q data, ['p', 'pretty'], 'no') == 'yes'136 onlyPublic = get_query_parameter(q data, ['op', 'onlypublic'], 'no') == 'yes'131 apiKey = get_query_parameter(queryParams, ['ak', 'apikey'], None) 132 signature = get_query_parameter(queryParams, ['signature']) 133 timestamp = get_query_parameter(queryParams, ['timestamp'], 0, integer=True) 134 no_cache = get_query_parameter(queryParams, ['nc', 'nocache'], 'no') == 'yes' 135 pretty = get_query_parameter(queryParams, ['p', 'pretty'], 'no') == 'yes' 136 onlyPublic = get_query_parameter(queryParams, ['op', 'onlypublic'], 'no') == 'yes' 137 137 138 138 # Get our handler function and its argument and response type 139 func, dformat = Exporter.parseRequest(path, qdata)139 func, dformat = HTTPAPIHook.parseRequest(path, queryParams) 140 140 if func is None or dformat is None: 141 141 raise apache.SERVER_RETURN, apache.HTTP_NOT_FOUND … … 198 198 199 199 serializer = Serializer.create(dformat, pretty=pretty, typeMap=typeMap, 200 **remove_lists(q data))200 **remove_lists(queryParams)) 201 201 202 202 if error: -
indico/web/http_api/util.py
r774511 rcd4e48 24 24 25 25 26 def get_query_parameter(q data, keys, default=None, integer=False):26 def get_query_parameter(queryParams, keys, default=None, integer=False): 27 27 if type(keys) != list: 28 28 keys = list(keys) 29 29 for k in keys: 30 paramlist = q data.get(k)30 paramlist = queryParams.get(k) 31 31 if paramlist: 32 32 if len(paramlist) == 1: … … 34 34 if integer: 35 35 val = int(val) 36 del q data[k]36 del queryParams[k] 37 37 return val 38 38 else: -
setup.py
r38596a rcd4e48 125 125 base = ['ZODB3>=3.8', 'pytz', 'zope.index', 'zope.interface', 126 126 'lxml', 'cds-indico-extras', 'zc.queue', 'python-dateutil<2.0', 127 'pypdf', 'mako>=0.4.1', 'babel' ]127 'pypdf', 'mako>=0.4.1', 'babel', 'icalendar', 'pyatom', 'python-memcached'] 128 128 129 129 #for Python older than 2.7
Note: See TracChangeset
for help on using the changeset viewer.
